﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Data;
using System.Linq;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Home.ArtDB
{
    /// <summary>
    /// 
    /// </summary>
    public class ArtFileCollection
    {
        /// <summary>Die Datei ist gefunden und die Tags sind ausgelesen</summary>
        public event EventHandler<ArtFileEventHandlerAargs> OnArtFileLoad;

        /// <summary>Die bereits erfasste Dateien werden aktualisiert</summary>
        public event EventHandler<ArtFileEventHandlerAargs> OnUpdateArtFileInDB;

        /// <summary>Neue Dateien werden hinzugefügt</summary>
        public event EventHandler<ArtFileEventHandlerAargs> OnAddNewArtFileToDB;

        /// <summary></summary>
        public event EventHandler<ArtFileEventHandlerAargs> OnArtFileError;

        /// <summary></summary>
        private ArtFileCollectionFilter _filter;

        /// <summary></summary>
        private DataSet _ds;

        /// <summary></summary>
        public DataTable Table;

        /// <summary></summary>
        private DataTable _filtertable;

        /// <summary></summary>
        public int Count { get { return Table.Rows.Count; } }

        /// <summary></summary>
        public int FileFilterCount { get { return Table.Rows.Count; } }

        /// <summary></summary>
        private string _loadedFile = "";

        /// <summary></summary>
        public string Caption { get { return _loadedFile.Equals(string.Empty) ? "-" : Path.GetFileNameWithoutExtension(_loadedFile); } }

        /// <summary>
        /// 
        /// </summary>
        public ArtFileCollection()
        {
            _ds = new DataSet();

            MemoryStream schemaStream = null;

            string schemacontent = Home.ArtDB.Properties.Resources.schema;

            schemaStream = new MemoryStream(System.Text.ASCIIEncoding.ASCII.GetBytes(schemacontent));

            _ds.ReadXmlSchema(schemaStream);

            Table = _ds.Tables[0];
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="filter"></param>
        public void SetFilter(ArtFileCollectionFilter filter)
        {
            if (filter != null)
            {
                _filter = filter;

                string select = "";
                foreach (string key in _filter.Keys.Split(' '))
                    select += string.Format(
                        "({1} like '%{0}%' or {2} like '%{0}%' or {3} like '%{0}%' or {4} like '%{0}%' or {5} like '%{0}%') and ",
                        key, ArtFile.GENRE, ArtFile.ARTIST, ArtFile.ALBUM, ArtFile.TITLE, ArtFile.COMMENT
                        );
                if (select.Length > 0)
                    select = select.Substring(0, select.Length - 4);

                _filtertable = Table.Clone();
                foreach (DataRow row in Table.Select(select))
                    _filtertable.ImportRow(row);
            }
            else
            {
                _filtertable = Table.Clone();
                foreach (DataRow row in Table.Rows)
                    _filtertable.ImportRow(row);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="filename"></param>
        public void Save(string filename)
        {
            _ds.WriteXml(filename);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public bool Open(string filename)
        {
            bool result = false;

            if (!filename.Length.Equals(0) && File.Exists(filename))
            {
                if (LoadXml(filename))
                {
                    _filtertable = _ds.Tables[0].Copy();
                    result = true;
                }
            }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="xmlFileName"></param>
        /// <returns></returns>
        private bool LoadXml(string xmlFileName)
        {
            _loadedFile = xmlFileName;

            try
            {
                _ds.ReadXml(xmlFileName);

                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Reset()
        {
            _loadedFile = "";
            Table.Rows.Clear();
            _filtertable.Rows.Clear();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="maxCount"></param>
        /// <returns></returns>
        private int GetEventStep(int maxCount)
        {
            int stepPerCent = 10;
            int eventStep = (maxCount * stepPerCent / 100) + 1;
            if (eventStep > 30) eventStep = 30;
            return eventStep;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileNames"></param>
        public void RemoveFiles(List<string> fileNames)
        {
            Table.Rows.Cast<DataRow>().Where(row => fileNames.Contains(row[ArtFile.FILENAME].ToString())).ToList().
                ForEach(row => Table.Rows.Remove(row));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="files"></param>
        public void AddFiles(List<string> files)
        {
            if (files.Count.Equals(0))
                return;

            //Dateien auslesen
            ArtFileCollection newFileCollection = new ArtFileCollection();
            newFileCollection.OnArtFileError = OnArtFileError;

            int eventStep = GetEventStep(files.Count);

            // Parallel.ForEach(files, filename =>
            foreach (string filename in files)
            {
                ArtFile artFile = newFileCollection.Add(ArtFile.Create(newFileCollection, filename));

                if (OnArtFileLoad != null && (newFileCollection.Count % eventStep).Equals(0))
                    OnArtFileLoad(this, new ArtFileEventHandlerAargs(artFile, newFileCollection.Count, files.Count));
            }

            string addedHomeIdListString = newFileCollection.Table.Rows.Cast<DataRow>().Select(item => "'" + item[ArtFile.HOMEID] + "'").ToString(",");

            //Geänderten Dateien
            DataRow[] changedRows = Table.Select(ArtFile.HOMEID + " in (" + addedHomeIdListString + ")");
            int existsRowIndex = 0;
            eventStep = GetEventStep(changedRows.Length);
            foreach (DataRow existsRow in changedRows)
            {
                foreach (DataRow changingRow in newFileCollection.Table.Rows.Cast<DataRow>().Where(item => item[ArtFile.HOMEID].Equals(existsRow[ArtFile.HOMEID])))
                {
                    foreach (DataColumn col in Table.Columns)
                    {
                        existsRow[col.ColumnName] = changingRow[col.ColumnName];
                    }
                }

                if (OnUpdateArtFileInDB != null && (existsRowIndex % eventStep).Equals(0))
                    OnUpdateArtFileInDB(this, new ArtFileEventHandlerAargs(null, ++existsRowIndex, changedRows.Length));
            }

            //Neue Dateien
            string changedHomeIdListString = changedRows.Select(item => "'" + item[ArtFile.HOMEID] + "'").ToString(",");

            if (changedHomeIdListString.Equals(string.Empty))
            {
                changedHomeIdListString = "''";
            }

            DataRow[] newRows = newFileCollection.Table.Select(ArtFile.HOMEID + " not in (" + changedHomeIdListString + ")");
            int newRowIndex = 0;
            eventStep = GetEventStep(newRows.Length);
            foreach (DataRow newRow in newRows)
            {
                Table.ImportRow(newRow);

                if (OnAddNewArtFileToDB != null && (existsRowIndex % eventStep).Equals(0))
                    OnAddNewArtFileToDB(this, new ArtFileEventHandlerAargs(null, ++newRowIndex, newRows.Length));
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="artFile"></param>
        private ArtFile Add(ArtFile artFile)
        {
            if (artFile != null && artFile.Valid)
            {
                if (artFile.Row.RowState.Equals(DataRowState.Detached))
                {
                    Table.Rows.Add(artFile.Row);
                }
            }
            else
            {
                if (OnArtFileError != null)
                    OnArtFileError(this, new ArtFileEventHandlerAargs(artFile, 0, 0));
            }

            return artFile;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<string> GetGenreList()
        {
            List<string> list = new List<string>();
            foreach (DataRow row in _ds.Tables[0].Select("", ArtFile.GENRE + " asc"))
                if (!list.Contains(row[ArtFile.GENRE].ToString()))
                    list.Add(row[ArtFile.GENRE].ToString());
            return list;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="genre"></param>
        /// <returns></returns>
        public List<string> GetArtistList(string genre)
        {
            List<string> list = new List<string>();

            string fieldname = ArtFile.ARTIST;

            string select = (genre != "") ? string.Format("{0}='{1}'", ArtFile.GENRE, genre) : "";

            foreach (DataRow row in Table.Select(select, fieldname + " asc"))
            {
                //string artist = (genre != "") ? row[ARTIST].ToString() : row[ARTIST].ToString()+" ("+genre+")";
                string artist = row[fieldname].ToString();

                if (!list.Contains(artist))
                    list.Add(artist);
            }
            return list;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="genre"></param>
        /// <returns></returns>
        public List<string> GetComposerList(string genre)
        {
            if (genre.Length.Equals(0))
            {
                return Table.Rows.Cast<DataRow>().Select(item => item[ArtFile.COMPOSER].ToString()).Distinct().ToList<string>();
            }
            else
            {
                return Table.Rows.Cast<DataRow>().Where(item => item[ArtFile.GENRE].ToString().Equals(genre)).Select(item => item[ArtFile.COMPOSER].ToString()).Distinct().ToList<string>();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="genre"></param>
        /// <param name="artist"></param>
        /// <param name="sourceField"></param>
        /// <returns></returns>
        public List<string> GetAlbumList(string genre, string artist, string sourceField)
        {
            List<string> list = new List<string>();

            string select = (genre != "") ? string.Format("{0}='{1}' and {2}='{3}'", ArtFile.GENRE, genre, sourceField, artist) : string.Format("{0}='{1}'", sourceField, artist);

            foreach (DataRow row in Table.Select(select, ArtFile.ALBUM + " asc"))
                if (!list.Contains(row[ArtFile.ALBUM].ToString()))
                    list.Add(row[ArtFile.ALBUM].ToString());
            return list;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="genre"></param>
        /// <param name="artist"></param>
        /// <param name="album"></param>
        /// <param name="atgroup"></param>
        /// <param name="sourceField"></param>
        /// <returns></returns>
        public DataTable GetTitleTable(string genre, string artist, string album, bool atgroup, string sourceField)
        {
            DataTable tbl = NewTitleTable();

            SortedList<string, DataRow> list = new SortedList<string, DataRow>();

            string select = (genre != "") ?
                string.Format("{0}='{1}' and {2}='{3}' and {4}='{5}'", ArtFile.GENRE, genre, sourceField, artist, ArtFile.ALBUM, album) :
                string.Format("{0}='{1}' and {2}='{3}'", sourceField, artist, ArtFile.ALBUM, album);

            string groupcolumn = atgroup ? ArtFile.TITLE : ArtFile.FILENAME;

            foreach (DataRow row in Table.Select(select, ArtFile.TRACK + " asc"))
            {
                if (!list.ContainsKey(row[groupcolumn].ToString()))
                {
                    DataRow newrow = tbl.NewRow();
                    tbl.Rows.Add(newrow);
                    newrow[ArtFile.TRACK] = (row[ArtFile.TRACK].ToString() != "") ? row[ArtFile.TRACK] : 0;
                    newrow[ArtFile.TITLE] = row[ArtFile.TITLE];
                    newrow[ArtFile.FILESCOUNT] = 0;
                    newrow[ArtFile.SIZE] = 0;
                    newrow[ArtFile.DURATION] = 0;
                    newrow[ArtFile.HOMEID] = row[ArtFile.HOMEID];

                    list.Add(row[groupcolumn].ToString(), newrow);
                }

                DataRow currentrow = list[row[groupcolumn].ToString()];

                currentrow[ArtFile.FILESCOUNT] = Convert.ToInt32(currentrow[ArtFile.FILESCOUNT]) + 1;

                if (currentrow[ArtFile.FILENAME].ToString().Length > 0)
                    currentrow[ArtFile.FILENAME] += "#\n";
                currentrow[ArtFile.FILENAME] += row[ArtFile.FILENAME].ToString();
                currentrow[ArtFile.SIZE] = Convert.ToDouble(currentrow[ArtFile.SIZE]) + Convert.ToDouble(row[ArtFile.SIZE]);
                currentrow[ArtFile.DURATION] = Convert.ToDouble(currentrow[ArtFile.DURATION]) + Convert.ToDouble(row[ArtFile.DURATION]);

                if (currentrow[ArtFile.COMMENT].ToString().Length > 0)
                    currentrow[ArtFile.COMMENT] += "#\n";
                currentrow[ArtFile.COMMENT] += row[ArtFile.COMMENT].ToString();
            }

            return tbl;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public DataTable NewTitleTable()
        {
            DataTable tbl = new DataTable();
            tbl.Columns.Add(ArtFile.TRACK);
            tbl.Columns.Add(ArtFile.TITLE);
            tbl.Columns.Add(ArtFile.FILESCOUNT);
            tbl.Columns.Add(ArtFile.COMMENT);
            tbl.Columns.Add(ArtFile.FILENAME);
            tbl.Columns.Add(ArtFile.SIZE, typeof(long));
            tbl.Columns.Add(ArtFile.DURATION, typeof(long));
            tbl.Columns.Add(ArtFile.HOMEID);

            return tbl;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public string CreateOverviewPage(string filename)
        {
            string result = Table.Rows.Cast<DataRow>().OrderBy(row => row[ArtFile.COMPOSER]).ThenBy(row => row[ArtFile.ALBUM]).
                Select(row => string.Format("<tr><td>{0}</td><td>{1}</td></tr>", row[ArtFile.COMPOSER], row[ArtFile.ALBUM])).
                Distinct().ToString(Environment.NewLine);

            StreamWriter sw = new StreamWriter(filename, false, Encoding.UTF8);
            try
            {
                sw.WriteLine("<html>");
                sw.WriteLine("<head>");
                sw.WriteLine("<title>"+Caption+"</title>");
                sw.WriteLine("<style type=\"text/css\">");
                sw.WriteLine("td  { white-space:nowrap; padding-left:20px;}");
                sw.WriteLine("</style>");
                sw.WriteLine("</head>");
                sw.WriteLine("<body>");
                sw.WriteLine(Caption);
                sw.WriteLine("<hr/>");
                sw.WriteLine("<table border=\"0\" rules=\"rows\">");
                sw.WriteLine("<colgroup>");
                sw.WriteLine("    <col width=\"*\">");
                sw.WriteLine("    <col width=\"100%\">");
                sw.WriteLine("</colgroup>");

                sw.WriteLine(result);

                sw.WriteLine("</table>");
                sw.WriteLine("<hr/>");
                sw.WriteLine("Stand "+DateTime.Now.ToShortDateString());
                sw.WriteLine("</body></html>");

                result = string.Empty;
            }
            catch (Exception ex)
            {
                result = ex.Message;
            }
            finally
            {
                sw.Close();
            }

            return result;
        }
    }
}
